/**
 * 新增动作
 */
function sAddAction(type){
    var _type = type;

    /**
     * 变异元素引用
     */
    var mutateEl = null;

    /**
     * 变异元素原始内容
     */
    var oldContent = null;

    /**
     * 新增元素集合
     */
    var elements = [];

    /**
     * 挂载的元素
     */
    var hangElements = [];

    /**
     * 变异元素的修改范围
     */
    var range = {
        start: 0,
        end: 0
    };

    /**
     * 选择区域
     */
    var selection_ranges = {};

    /**
     * 动作发生所在偏移量
     */
    var _offset = null;

    /**
     * 动作发生时的选项
     */
    var _opt = null;

    /**
     * 反转前置函数
     */
    var _reverse = null;

    /**
     * 恢复前置函数
     */
    var _recover = null;

    /**
     * 留痕状态
     */
    var _leaveTrace = false;

    /**
     * 获取动作类型
     */
    this.getActType = function(){
        return ssDict.sActionType.INSERT;
    }

    /**
     * 获取区域类型
     * @returns 
     */
    this.getType = function(){
        return _type;
    }

    /**
     * 设置留痕状态
     * @param {*} status true留痕 false不留痕
     */
    this.setLeaveTrace = function(status){
        _leaveTrace = status;
    }

    /**
     * 设置选择区域
     * @param {*} selectionRange 选择范围对象
     * @param {*} before 动作之前
     */
    this.setSelectionRange = function(selectionRange, before){
        selection_range = {
            startOffset: null,
            endOffset: null,
            __position: selectionRange.__position
        };

        selection_range.startOffset = this.deepCloneOffset(selectionRange.startOffset);
        selection_range.endOffset = this.deepCloneOffset(selectionRange.endOffset);

        selection_ranges[before ? "reverse" : "recover"] = selection_range;
    }

    /**
     * 设置动作发生时偏移量
     * @param {*} offset 
     * @param {*} opt 
     */
    this.setOffset = function(offset, opt) {
        _offset = offset;
        _opt = opt;
    }

    /**
     * 反转前置函数
     * @param {*} beforeReverse 
     */
    this.setBeforeReverse = function(beforeReverse){
        _reverse = beforeReverse;
    }

    /**
     * 恢复前置函数
     * @param {*} beforeRecover 
     */
    this.setBeforeRecover = function(beforeRecover){
        _recover = beforeRecover;
    }

    /**
     * 返回反转前置函数
     * @returns 
     */
    this.beforeReverse = function(){
        return _reverse;
    }

    /**
     * 返回恢复前置函数
     * @returns 
     */
    this.beforeRecover = function(){
        return _recover;
    }

    /**
     * 添加文本元素
     * @param {*} element 文本元素
     * @param {*} merge 是否合并变异元素的分离内容(文本元素支持)
     * @param {*} delEmpty 是否删除内容为空的变异元素
     */
    this.addText = function(element, merge, delEmpty){
        var offset = null;
        var el = {
            element: element,
            oldParagraphIndex: element.__paragraphIndex
        };

        if(element._sType == "sText" && 
           element._sType == mutateEl.element._sType && merge){

            var tempRange = {
                start: element.innerText.length - 1,
                end: element.innerText.length
            };

            element.style = {...mutateEl.element.style};
            element.innerText += oldContent.substr(range.end);    
            el.newContent = element.innerText;
            el.oldContent = "";
            
            if(delEmpty && !mutateEl.element.innerText){
                var index = 0;
                for(var i in elements){
                    if(elements[i].element == mutateEl.element){
                        index = i;
                        break;
                    }
                }

                elements.splice(index, 1);
            }

            var elRange = this.getElementPageRange(mutateEl.element);

            offset = this.deepCloneOffset(mutateEl.offset);
            offset.range = tempRange;

            this.mergeOffsetNext(offset, {
                pageIndex: elRange.startPageIndex,
                rowIndex: elRange.startRowIndex
            });

            el.offset = offset;
        }

        el.mutateNew = true;

        elements.push(el);

        return this;
    }

    /**
     * 变异文本元素，只能执行一次
     * @param {*} element 文本元素
     * @param {*} offset 元素偏移量
     * @param {*} style 变异样式
     */
    this.mutateText = function(element, offset, style){
        var rootOffset = this.deepCloneOffset(offset);
        var offset = this.extractDeepOffset(rootOffset);

        var innerText = "";
        var hasPlaceholder = element.placeholder != null && element.placeholder.length > 0;
        var hasInnerText = element.innerText != null && element.innerText.length > 0;

        if(hasInnerText || (!hasInnerText && !hasPlaceholder)){
            var pText = element.__texts[offset.pageIndex];
            var rText = pText[offset.rowIndex];
    
            rText = rText.substr(0, offset.contentIndex + 1);
    
            for(var i in element.__texts){
                pText = element.__texts[i];
                for(var j in pText){
                    if(i < offset.pageIndex || (i == offset.pageIndex && j < offset.rowIndex)){
                        range.start += pText[j].length;
                        innerText += pText[j];
                    }else if(i == offset.pageIndex && j == offset.rowIndex){
                        innerText += rText;
                    }
                }
            }
        }
        
        range.start += offset.contentIndex;
        range.end = range.start + 1;

        rootOffset.range = range;

        oldContent = element.innerText;

        var el = {
            element: element,
            offset: rootOffset,
            oldContent: element.innerText,
            newContent: innerText,
            mutate: true,
            oldParagraphIndex: element.__paragraphIndex
        }

        if(el.newContent == el.oldContent){
            el.mutate = false;
        }

        if(style){
            el.oldStyle = element.style,
            el.newStyle = {...element.style, ...style};
            element.style = el.newStyle;
        }

        elements.push(el);
        element.innerText = innerText;

        mutateEl = el;

        return this;
    }

    /**
     * 将一个文本元素按指定偏移量分割成两个元素，并在中间插入一个新元素
     * @param {*} text 文本元素
     * @param {*} offset 偏移量
     * @param {*} element 插入元素
     * @param {*} elementOffset 偏移量
     * @param {*} opt 可选项
     */
    this.divideTextAndInsertElement = function(text, offset, element, elementOffset, opt){
        opt = opt ? opt : {};

        var next = this.extractDeepOffset(offset);
        this.mergeOffsetNext(offset, {
            start: next.contentIndex,
            end: next.contentIndex
        });

        var splitText = this.splitText(text, [offset]);

        var result = this.composeElements(splitText, text, offset, this.wrapElement, opt);

        if(result.leftElement){
            this.mergeOffsetNext(result.leftElement.offset, {
                range: {
                    start: next.contentIndex + 1,
                    end: next.contentIndex + 1
                }
            });
            elements.push(result.leftElement);
        }

        if(!elementOffset){
            var elementIndex = null;

            if(result.leftElement){
                elementOffset = this.deepCloneOffset(result.leftElement.offset);
                elementIndex = next.elementIndex + 1;
            }else{
                elementOffset = this.deepCloneOffset(offset);
                elementIndex = next.elementIndex;
            }

            var contentIndex = element.innerText != null ? elementIndex.innerText.length : 0;

            this.mergeOffsetNext(elementOffset, {
                elementIndex: elementIndex,
                contentIndex: contentIndex,
                range: {
                    start: contentIndex,
                    end: contentIndex
                }
            })
        }

        result.inElement = this.wrapElement(element, elementOffset);
        result.inElement.mutateNew = true;
        delete result.inElement.mutate;

        if(opt.main){
            result.inElement.main = true;
        }

        if(_leaveTrace){
            result.inElement.element.new = true;
            result.inElement.element.style.doubleDash = "bottom";
        }

        elements.push(result.inElement);

        if(opt.elementProp){
            for(var i in opt.elementProp){
                result.inElement.element[i] = opt.elementProp[i];
            }
        }

        if(result.rightElement){
            elements.push(result.rightElement);
            next = this.extractDeepOffset(result.inElement.offset);
            
            this.mergeOffsetNext(result.inElement.offset, {
                elementIndex: next.elementIndex + 1,
                range: {
                    start: -1,
                    end: 0
                }
            });
            
            this.changeAnnotation(result.leftElement.element, result.rightElement.element);
        }

        return this;
    }

    /**
     * 添加元素
     * @param {*} element 元素
     * @param {*} offset 元素所在偏移量
     * @param {*} style 变异样式
     * @param {*} main 主元素标记
     * @param {*} index 指定位置插入
     */
    this.add = function(element, offset, style, main, index){
        if(main){
            offset = offset ? offset : {};
            if(element._sType == "sText"){
                offset.range = {
                    start: element.innerText.length,
                    end: element.innerText.length
                }
            }else if(!offset.range){
                offset.range = {
                    start: 1,
                    end: 1
                }
            }
        }

        var el = {
            element: element,
            offset: this.deepCloneOffset(offset),
            mutateNew: true,
            oldParagraphIndex: element.__paragraphIndex
        };

        if(main){
            //只能有一个主元素
            for(var i in elements){
                if(elements[i].main){
                    delete elements[i].main;
                }
            }
            el.main = main;
        }

        if(index){
            elements.splice(index, 0, el);
        }else{
            elements.push(el);
        }

        if(style){
            el.oldStyle = element.style;
            el.newStyle = {...element.style, ...style};
            element.style = el.newStyle;
        }

        if(_leaveTrace){
            el.element.new = true;
            el.element.style.doubleDash = "bottom";
        }

        return this;
    }

    /**
     * 挂载段落序号改变的元素
     * @param {*} element 元素
     * @param {*} oldParagraphIndex 旧的段落序号 
     */
    this.hang = function(element, oldParagraphIndex){
        if(hangElements.indexOf(element) > -1) return;
        hangElements.push(element);

        elements.push({
            element: element,
            oldParagraphIndex: oldParagraphIndex,
            newParagraphIndex: element.__paragraphIndex,
            hang: true
        });

        return this;
    }

    /**
     * 获取所有元素
     * @returns 
     */
    this.getElements = function(){
        if(hangElements.length > 0){
            var els = [];

            for(var i in elements){
                if(elements[i].hang) continue;
                els.push(elements[i]);
            }

            return els;
        }

        return elements;
    }

    /**
     * 获取最后一个元素
     * @returns 
     */
    this.getLastElement = function(){
        var els = this.getElements();
        return els.length > 0 
            ? els[els.length - 1]
            : null;
    }

    /**
     * 根据元素获取对应的action记录
     * @param {*} element 
     * @param {*} offset
     * @param {*} opt
     * @returns 
     */
    this.mutateParagraph = function(element, offset, opt){
        opt = opt ? opt : {};

        var el = null;

        for(var i in elements){
            if(elements[i].element === element){
                el = elements[i];
                break;
            }
        }

        if(!el){
            el = {
                element: element,
                offset: this.extractOffset(offset)
            }

            elements.push(el);
        }

        if(opt.style){
            el.oldStyle = element.style;
            el.newStyle = {...element.style, ...opt.style};

            element.style = el.newStyle;
        }

        if(opt.relFor){
            el.oldRelFor = element.__relFor;
            el.newRelFor = opt.relFor;

            element.__relFor = opt.relFor;
        }
    }

    /**
     * 获取变异元素的修改范围
     * @returns 
     */
    this.getRange = function(){
        return range;
    }

    /**
     * 返回当前动作的反向动作
     */
    this.reverse = function(){
        var result = {
            type: _type,  
            elements: [],
            offset: _offset,
            selectionRange: selection_ranges.reverse
        };

        var el = null;
        var elc = null;

        for(var i in elements){
            el = elements[i];

            if(el.mutateNew){
                elc = {...el};
                elc.delete = true;
                elc.delRange = this.getElementPageRange(elc.element);

                delete elc.mutateNew;

                result.delete = true;
            }else{
                elc = {...el};

                if(elc.mutate){
                    delete elc.mutate;
                }
                
                if(elc.oldContent != null){
                    elc.element.innerText = elc.oldContent;
                }

                if(elc.oldStyle != null){
                    elc.element.style = elc.oldStyle;
                }

                if(elc.element._sType != "sParagraph"){
                    if(elc.oldParagraphIndex != null){
                        //挂载元素的新旧段落序号已经确定，不需要在更改
                        if(elc.hang) {
                            elc.element.__paragraphIndex = elc.oldParagraphIndex;
                            continue;
                        }
                        el.newParagraphIndex = elc.element.__paragraphIndex;
                        elc.element.__paragraphIndex = elc.oldParagraphIndex;
                    }
                }else{
                    delete elc.element.__relFor;
                }

                if(elc.offset){
                    if(!elc.offset.pageIndex){
                        var elRange = this.getElementPageRange(elc.element);
                        elc.offset = {
                            pageIndex: elRange.startPageIndex,
                            rowIndex: elRange.startRowIndex
                        };

                        if(el.element.innerText){
                            elc.offset.range = {
                                start: elc.element.innerText.length,
                                end: elc.element.innerText.length
                            }
                        }else{
                            elc.offset.range = {
                                start: 1,
                                end: 1
                            }
                        }
                    }else{
                        elc.offset = this.deepCloneOffset(el.offset);
                    }                    
                }

                this.clearMiddleProperties(elc);
            }

            if(!result.offset && elc.offset){
                result.offset = elc.offset;
            }

            if(elc.hang) continue;
            
            result.elements.push(elc);
        }

        return result;
    }

    /**
     * 将反向动作恢复成正向动作
     */
    this.recover = function(){
        var result = {
            type: _type,
            elements: [],
            offset: _offset,
            opt: _opt,
            selectionRange: selection_ranges.recover
        };

        var el = null;
        var elc = null;

        for(var i in elements){
            el = elements[i];

            elc = {...el};

            if(elc.newContent != null){
                elc.element.innerText = elc.newContent;
            }

            if(elc.newStyle != null){
                elc.element.style = elc.newStyle;
            }

            if(elc.element._sType != "sParagraph"){
                if(elc.newParagraphIndex != null){
                    elc.element.__paragraphIndex = elc.newParagraphIndex;
                }
            }else{
                delete elc.element.__relFor;
            }

            if(elc.hang) continue;

            elc.offset = this.deepCloneOffset(el.offset);

            result.elements.push(elc);

            this.clearMiddleProperties(elc);
        }

        return result;
    }

    this.link = function(element, elarr){
        if(!elarr) return;

        let nextSibling = element.nextSibling;

        if(Array.isArray(elarr) && elarr.length > 0){
            this.linkElement(element, elarr[0]);

            for(let i = 0; i < elarr.length; i++){
                this.linkElement(elarr[i], elarr[i+1]);
            }
    
            this.linkElement(elarr[elarr.length - 1], nextSibling);
        }else if(elarr._sType){
            this.linkElement(element, elarr);
            this.linkElement(elarr, nextSibling);
        }
    }
}